/*
 *	Utopia ME Wrapper Module 5.00 clone
 *	Copyright (c) 2011 by Musa Mitchell <mowglisanu@gmain.com>
 */
/*
Tested:
	MP4 video
	PMF video
	UMD audio/video
	mp3/aac/wma/at3/at3+
	standby/resume/poweroff
 */
#include <pspkernel.h>
#include <pspctrl.h>
#include <pspsysevent.h>
#include <pspsysmem_kernel.h>
#include <stdarg.h>
#include "me_wrapper.h"
#include "stdio.h"

PSP_MODULE_INFO("sceMeCodecWrapper", 0x1007, 1, 9);

u32 module_sdk_version = 0x05000010;
int meStarted;
MERpc meRpc;
int interruptHandler(int a0, int SceMediaEngineRpcWait){
	sceKernelSetEventFlag(SceMediaEngineRpcWait, 1);
	return -1;
}
int initRpc(){
	if ((meRpc.mutex = sceKernelCreateMutex("SceMediaEngineRpc", 0x101, 0, 0)) < 0)return meRpc.mutex;
	if ((meRpc.sema = sceKernelCreateSema("SceMediaEngineAvcPower" , 0x101, 1, 1, 0)) < 0)return meRpc.sema;
	if ((meRpc.event = sceKernelCreateEventFlag("SceMediaEngineRpcWait", 0x201, 0, 0)) < 0)return meRpc.event;
	sceSysregIntrEnd();
	int ret = sceKernelRegisterIntrHandler(PSP_MECODEC_INT, 2, (void*)&interruptHandler, (void*)meRpc.event, 0);
	if (ret < 0)return ret;
	return sceKernelEnableIntr(PSP_MECODEC_INT);
}
int sub_1026(){
	int ret = 0;
	volatile int* address = (int*)0xbfc00700;
	if (*address == 0){
		ret = sceKernelTryLockMutex(meRpc.mutex, 1);
		if (ret>=0){
			sceKernelUnlockMutex(meRpc.mutex, 1);
		}
	}
	else{
		if (*address != -1){
			*address = -2;
		}
	}
	return ret;
}
int sub_0x10005(){
	return sub_881FA1D8((void*)(0x80000000|((u32)SysMemForKernel_5339A163/*0xCDA3A2F7*/()&0x1fffffff)), 0);
}
int sub_16389(){
	volatile int *hwAddress = (int*)0xbfc00700;
	*hwAddress = -1;
	int* meTable = (int*)0xbfc00600;
	meTable[0] = 399;//copy SysMemForKernel_374E8E66 bytes on me main memory to SysMemForKernel_5339A163 then halt me
	meTable[2] = (int)SysMemForKernel_5339A163();//0xCDA3A2F7
	meTable[3] = SysMemForKernel_374E8E66();//0xB9F8561C
	meTable[4] = 0;
	meTable[5] = 0;
	meTable[6] = 0;
	meTable[7] = 0;
	meTable[8] = 0;
	meTable[9] = 0;
	sceDdrFlush(5);
	sceSysregInterruptToOther();
	while (*hwAddress != -2);
	sceDdrFlush(8);
	printf("sysrpc 399 0x%x\n", meTable[10]);
	sceSysregMeResetEnable();
	sceSysregMeBusClockDisable();
	*(int*)0xbfc00718 = *(int*)0xbc100070 & 4;
	sceKernelClearEventFlag(meRpc.event, 0);
	return 0;
}
int sub_0x100000(){
	 sceMeRpcLock();
	if (*(int*)0xbfc00718 == 0)
		sceSysconCtrlTachyonAvcPower(0);
	sceMeRpcUnlock();
	return 0;
}
int sub_0x1000020(){
	volatile int* address = (int*)0xbfc0070c;
	int first = *address;
	int second = *address;
	int* meTable = (int*)0xbfc00600;
	meTable[0] = 391;
	meTable[2] = (first & 0x1ff0000)>>16;
	meTable[3] = second & 0x1ff;
	meTable[4] = 0;
	meTable[5] = 0;
	meTable[6] = 0;
	meTable[7] = 0;
	meTable[8] = 0;
	meTable[9] = 0;
	sceDdrFlush(5);
	sceSysregInterruptToOther();
	sceKernelWaitEventFlag(meRpc.event, 1, PSP_EVENT_WAITCLEAR, 0, 0);
	printf("sysrpc 391 0x%x\n", meTable[10]);
	sceKernelUnlockMutex(meRpc.mutex, 1);
	return 0;
}
/*
dark
0xBFC0070C: 0x00010002
0xBFC00710: 0x0000005F
0xBFC00714: 0x00000002
light
0xBFC0070C: 0x00010002
0xBFC00710: 0x000000DE
0xBFC00714: 0x00000002
standby
17 phase 2
30 phase 1
restart
17 phase 2
42 phaze 1
poweroff
17 phase 2
40 phaze 1
*/
int sub_0x1000002(int arg){
	sceKernelLockMutex(meRpc.mutex, 1, 0);
	int argSr = arg >> 1;
	int val = *(int*)0xbfc00714;
	int comp1 = argSr < 133;
	int comp2 = argSr < 148;
	u32 swapBit0 = val^1;
	int isValEq1 = swapBit0 < 1;
	u32 swapBits0And1 = val^3;
	int notComp1 = comp1^1;
	int notComp2 = comp2^1;
	int isValEq3 = swapBits0And1 < 1;
	int result1 = notComp1 & isValEq1;
	int result2 = notComp2 & isValEq3;
	*(int*)0xbfc00710 = arg;
	int arg0 = 0;
	if (result1 == 0){//if (((arg >> 1) < 133) || (val != 1))
		if (result2 == 0){//if (((arg >> 1) < 148) || (val != 3))
			return 0;//if (((arg >> 1) < 148) || (val != 3) || (val != 1))
		}
		else{
			arg0 = 2;
		}
	}
	int* meTable = (int*)0xbfc00600;
	meTable[0] = 7;//sets 0xbfc00714 to arg0 then calls sceSysregMsifClkSelect(0, arg0)
	meTable[2] = arg0;//if (val==1 && ((arg>>1)>=133)) arg = 0, if (val==3 && ((arg>>1)>=133)) arg0 = 2
	meTable[3] = 0;
	meTable[4] = 0;
	meTable[5] = 0;
	meTable[6] = 0;
	meTable[7] = 0;
	meTable[8] = 0;
	meTable[9] = 0;
	sceDdrFlush(5);
	sceSysregInterruptToOther();
	sceKernelWaitEventFlag(meRpc.event, 1, PSP_EVENT_WAITCLEAR, 0, 0);
	printf("sysrpc 7 0x%x\n", meTable[10]);
	return 0;	
}
int sub_0x1000f(){
	volatile int *hwAddress = (int*)0xbfc00700;
	while (*hwAddress != -4);
	if (*(int*)0xbfc00718 == 0){
		*(int*)0xbc100070 = *(int*)0xbc100070 & (~4);//clear 3rd bit
		sceSysregAvcResetEnable();
	}
	*(int*)0xbfc00700 = 0;
	return 0;
}
/*start up
0x01000000
0x01000002
0x01000010 phase0
0x01000011 phase1
0x01000020
*/
/* loading eboot ev_ids, back light on/off
0x1000000
0x1000002
0x1000020*/

int eventHandler(int ev_id, char *ev_name, void *param, int *result){
	sceIoWrite(2,"EVENT\n\n",7);
	printf("event 0x%x %d %s %p %p %d\n", ev_id, ev_id, ev_name?ev_name:"", param, result, result?*result:0);
	if (!meStarted)
		return 0;
	switch (ev_id){
		case 1026:
			return sub_1026();
		break;
		case 0x10005:
			return sub_0x10005();
		break;
		case 16389:
			return sub_16389();
		break;
		case 0x100000:
			return sub_0x100000();
		break;
		case 0x1000020:
			sub_0x1000020();
		break;
		case 0x1000002:
			sub_0x1000002(((int*)param)[1]);
		break;
		case 0x1000f:
			return sub_0x1000f();
		break;
	}
	return 0;
}
PspSysEventHandler SceMeRpc = {sizeof(PspSysEventHandler), "SceMeRpc", 0x01ffff00, &eventHandler, 0,0, NULL, {0,0,0,0,0,0,0,0,0}};

int module_start(int argc, char *argv[]){//sceMeWrapperInit
#ifdef DEBUG
	sceKernelDelayThread(2000000);
#endif
//	printf_init();
	initRpc();
	sceMeBootStart(2);
	*(int*)0xbfc00710 = (int)sceSysregPllGetFrequency();
	/* This is what makes mediaengine.prx hang on standby/poweroff */
	sceKernelRegisterSysEventHandler(&SceMeRpc);
	sceMePowerControlAvcPower(0);
	if (sceSysregGetTachyonVersion() > 0x4fffff){
		PspSysmemPartitionInfo info;
		info.size = sizeof(PspSysmemPartitionInfo);
		if (sceKernelQueryMemoryPartitionInfo(4, &info) < 0){
			sceKernelAllocPartitionMemory(1, "old ME partition", 2, 0x100000, (void*)0x88300000);
		}
		else if (info.startaddr != 0x88300000){
			sceKernelAllocPartitionMemory(1, "old ME partition", 2, 0x100000, (void*)0x88300000);
		}
	}
	return 0;
}

int sceMeWrapperEnd(){//0x8D46A04D
	printf("sceMeWrapperEnd\n");
	sceSysregVmeResetEnable();	
	sceSysregAvcResetEnable();	
	sceSysregMeResetEnable();	
	sceSysregMeBusClockDisable();	
	sceKernelUnregisterSysEventHandler(&SceMeRpc);
	return 0;
}	

int module_reboot_phase(int argc, char *argv[]){
	printf("module_reboot_phase\n");
	if (argc == 1){
		sceMeWrapperEnd();
	}
	return 0;
}

/**************************sceMePower_driver****************************/
inline int shiftK1(){
	int ret;
	asm volatile (
	"move %0, $27\n"
	"srl $27, $27, 16\n"
	:"=r"(ret):);
	return ret;
}
inline void setK1(int k1){
	asm volatile (
	"move $27, %0\n"
	::"r"(k1));
}
int sceMeCore_driver_F702E73A(int index,...){//working
	va_list ap;
	va_start(ap,index);
	int ret = sceKernelLockMutex(meRpc.mutex, 1, 0);
	if (ret < 0){
		printf("rpc mutex %d %x\n", index, ret);
		return ret;
	}
	int* meTable = (int*)0xbfc00600;
	meTable[0] = index;
	meTable[2] = va_arg(ap,int);
	meTable[3] = va_arg(ap,int);
	meTable[4] = va_arg(ap,int);
	meTable[5] = va_arg(ap,int);
	meTable[6] = va_arg(ap,int);
	meTable[7] = va_arg(ap,int);
	meTable[8] = va_arg(ap,int);
	meTable[9] = va_arg(ap,int);
	va_end(ap);
	
	sceDdrFlush(5);
	sceSysregInterruptToOther();
	sceKernelWaitEventFlag(meRpc.event, 1, PSP_EVENT_WAITCLEAR, 0, 0);
	ret = meTable[10];
	//printf("rpc %d %x\n", index, ret);
	sceKernelUnlockMutex(meRpc.mutex, 1);
	return ret;
}
int sceMeCore_driver_278F2C25(int index,...){
	sceKernelDcacheWritebackInvalidateAll();
	va_list ap;
	va_start(ap,index);
	int ret=sceMeCore_driver_F702E73A(index, va_arg(ap, int), va_arg(ap, int), va_arg(ap, int), va_arg(ap, int), va_arg(ap, int), va_arg(ap, int), va_arg(ap, int), va_arg(ap, int));
	va_end(ap);
	return ret;
}
int sceMePower_driver_23481212(int numerator, int denominator){//55F11AF6
	int k1 = shiftK1();
	int ret = sceMeCore_driver_F702E73A(390, numerator, denominator);//setCpuGranuality
	setK1(k1);
	return ret;
}
int sceMePower_driver_68B70336(int numerator, int denominator){//8EBBBBA3
	int k1 = shiftK1();
	int ret = sceMeCore_driver_F702E73A(391, numerator, denominator);//setBusGranuality,meSysregDmac0BusClockEnable,...,meSysregDmac0BusClockEnable
	setK1(k1);
	return ret;
}
int sceMePowerSelectAvcClock(int arg0){
	printf("sceMePowerSelectAvcClock\n");
	int k1 = shiftK1();
	int ret = sceMeCore_driver_F702E73A(7, arg0);//meSysregMsifClkSelect(0, arg0)
	setK1(k1);
	return ret;
}
int sceMePower_driver_52E7CD99(int enable){
	int k1 = shiftK1();
	int ret = sceMeCore_driver_F702E73A(enable?387:388);
	setK1(k1);
	return ret;
}
void sceMePowerControlAvcPower(int arg0){
	int k1 = shiftK1();
	if (sceKernelWaitSema(meRpc.sema, 1, 0) < 0){
		return;
	}
	volatile int* hwAddress = (int*)0xbc100070;
	int val = *hwAddress & 4;
	if (arg0 == 0){
		if (val != 0){
			sceSysregAvcResetEnable();
			int intr = sceKernelCpuSuspendIntr();
			*hwAddress = *hwAddress & 0xfffffffb;//clear bit 2
			sceKernelCpuResumeIntr(intr);
			sceSysconCtrlTachyonAvcPower(0);
		}
	}
	else if (val == 0){
		sceSysconCtrlTachyonAvcPower(arg0);
		int intr = sceKernelCpuSuspendIntr();
		*hwAddress = *hwAddress | 4;
		sceKernelCpuResumeIntr(intr);
		sceMeCore_driver_F702E73A(8);
		sceSysregAvcResetDisable();
	}
	*(int*)0xbfc00718 = *hwAddress & 4;
	sceKernelSignalSema(meRpc.sema, 1);
	setK1(k1);
}
/**************************sceMeVideo_driver****************************/
int sub_881F9560(u32 *arg0, int error){
	printf("sub_881F9560\n");
	if (error == 0x80000003) return -1;
	arg0[2] = error;
	return (error ^ -8)?-3:-4;//-8 = 0xfffffff8
//	if (error == -8) return -4;
//	return -3;
}
int sub_881F9590(u32 *arg0, int val){//is val an error?
	printf("sub_881F9590\n");
	if (val == 0x80000003) return -1;
	arg0[2] = val;
	return (val >= 0)?-3:-4;
}
int sceMeVideo_driver_F24820D4(int arg0, u32 *arg1){
	printf("sceMeVideo_driver_F24820D4\n");
	if (0x5100601 < arg1[0]) return -2;
	if (arg1[4] == 0) return -1;
	int ret;
	if (arg0 == 0){
		ret = sceMeCore_driver_278F2C25(0, arg1[4]);
		arg1[6] = ret;
		if (ret == 0){
			arg1[2] = -8;			
			arg1[8] = 0;
			return -3;
		}
		else{
			arg1[2] = 1;
			arg1[8] = ((u32*)arg1[4])[1];
			return 0;
		}
	}
	else if (arg0 == 1){
		u32 val = sceMeCore_driver_F702E73A(36);
		ret = sceMeCore_driver_F702E73A(35, arg1[11], arg1[12], arg1[13]);
		arg1[6] = val;
		arg1[8] = ret;
		if ((ret <= 0) || (val <= 0)){
			arg1[2] = 0x80000000;
			return -3;
		}
		else{
			arg1[2] = 0;
			return 0;
		}
	}
	return -1;
}
int unkVideo;
int sceMeVideo_driver_93EED793(int arg0, u32 *arg1){//06F0236A
	printf("sceMeVideo_driver_93EED793\n");
	if (0x5100601 < arg1[0]) return -2;
	arg1[2] = 0;
	int intr = sceKernelCpuSuspendIntr();//huh?
	if (unkVideo != 0){
		sceKernelCpuResumeIntr(intr);
		return -4;
	}
	unkVideo = 1 << arg0;
	sceKernelCpuResumeIntr(intr);
	int ret;
	if (arg0 == 1){
		ret = sceMeCore_driver_278F2C25(37, &arg1[3], arg1[5], arg1[6], 0);
		if (ret != 0){
			return sub_881F9590(arg1, ret);
		}
	}
	else if (arg0 == 0){
		ret = sceMeCore_driver_278F2C25(1, &arg1[3], arg1[5], arg1[7]);
		if (ret < 0){
			return sub_881F9560(arg1, ret);
		}
		else{
			sceMePowerControlAvcPower(1);
		}		
	}
	else if (arg0 != 3){
		return -1;
	}
	sceMePower_driver_52E7CD99(1);
	return 0;
}
//decode
int sceMeVideo_driver_2C38DB84(int arg0, u32 *arg1){
	printf("sceMeVideo_driver_2C38DB84\n\n");
	if (0x5100601 < arg1[0]) return -2;
	int ret;
	arg1[2] = 0;
	if (arg0 == 0){
		int count = arg1[15] - 1;
		if (count >= 4){
			return -1;
		}
		else{
			u32 *loc = (u32*)((u32)(count*44)+arg1[11]);
			if (arg1[14] != 0){
				u32 val = (u32)(count*328)+arg1[14];
				do{
					loc[9] = val;
					loc[10] = val + 164;
					val -= 328;
					loc -= 11;//44 bytes
					count -= 1;
				}
				while (count >= 0);
			}
			else{
				do{
					loc[9] = 0;
					loc[10] = 0;
					count -= 1;
					loc -= 11;//44 bytes
				}
				while (count >= 0);
			}
			sceKernelDcacheWritebackInvalidateRange((void*)arg1[11], arg1[15]*44);
			sceKernelDcacheWritebackInvalidateRange(&arg1[11], 48);
			ret = sceMeCore_driver_F702E73A(2, arg1[3], arg1[9], arg1[10], arg1[4], arg1[11], arg1[12], arg1[13], &arg1[11]);
			if (ret < 0){
				return sub_881F9560(arg1, ret);
			}
			return 0;
		}
	}
	else if (arg0 == 1){
		((u32*)arg1[4])[0] = arg1[9];
		((u32*)arg1[4])[1] = arg1[10];
		((u32*)arg1[4])[2] = arg1[14];
		((u32*)arg1[4])[3] = 64;
		((u32*)arg1[4])[4] = 0;
		sceKernelDcacheWritebackInvalidateRange((void*)arg1[4], 256);
		ret = sceMeCore_driver_F702E73A(32, arg1[3], arg1[4], arg1[7], arg1[8]);
		if (ret != 0){
			return sub_881F9590(arg1, ret);
		}
		return 0;
	}
	return -1;
}
//Stop
int sceMeVideo_driver_6401013A(int arg0, u32 *arg1){//DE2E7E89
	printf("sceMeVideo_driver_6401013A\n");
	if (0x5100601 < arg1[0]) return -2;
	arg1[2] = 0;
	int ret;
	if (arg0 == 0){
		int count = (arg1[15] - 1);
		if ((u32)count >= 4){
			return -1;
		}
		else{
			u32 *loc = (u32*)(((u32)count*44)+arg1[11]);
			if (arg1[14] != 0){
				u32 val = ((u32)count*328)+arg1[14];
				u32 *loc = (u32*)(((u32)count*44)+arg1[11]);
				do{
					loc[9] = val;
					loc[10] = val + 164;
					val -= 328;
					count--;           
					loc -= 11;//44 bytes
				}
				while (count >= 0);
			}
			else{
				do{
					loc[9] = 0;
					loc[10] = 0;
					count--;           
					loc -= 11;//44 bytes
				}
				while (count >= 0);				
			}
			ret = sceMeCore_driver_278F2C25(3, arg1[3], arg1[4], arg1[11]);
			if (ret < 0){
				return sub_881F9560(arg1, ret);
			}
			return 0;
		}
	}
	else if (arg0 == 1){
		ret = sceMeCore_driver_278F2C25(33, arg1[3]);
		if (ret != 0){
			return sub_881F9590(arg1, ret);
		}
		return 0;
	}
	return -1;
}
//Delete, FinishMJPEG
int sceMeVideo_driver_2DAAC3A9(int arg0, u32 *arg1){
	printf("sceMeVideo_driver_2DAAC3A9\n");
	if (0x5100601 < arg1[0]) return -2;
	int ret;
	int intr = sceKernelCpuSuspendIntr();
	if (((unkVideo >> arg0) & 1) == 0){
		sceKernelCpuResumeIntr(intr);
		return -4;
	}
	unkVideo = unkVideo - (1 << arg0);
	sceKernelCpuResumeIntr(intr);
	arg1[2] = 0;
	if (arg0 == 0){
		ret = sceMeCore_driver_278F2C25(4, arg1[3]);
		if (ret < 0){
			return sub_881F9560(arg1, ret);
		}
		sceMePowerControlAvcPower(0);
	}
	else if (arg0 == 1){
		ret = sceMeCore_driver_278F2C25(34, arg1[3]);
		if (ret != 0){
			return sub_881F9590(arg1, ret);
		}
	}
	else if (arg0 != 3){
		return -1;
	}
	if (unkVideo == 0){
		sceMePower_driver_52E7CD99(0);
	}
	return 0;
}
//GetFrameCrop, SetMemory, ScanHeader, GetVersion, GetSEI, _893B32B1
int sceMeVideo_driver_994BBA92(int arg0, u32 arg1, u32 *arg2){//1C6A60E1
	printf("sceMeVideo_driver_994BBA92\n");
	if (0x5100601 < arg2[0]) return -2;
	int ret;
	switch (arg1){
		case 0:
		case 4:
		default:
			return -1;
		break;
		case 1:
			if (arg0 != 0){
				return -1;
			}
			arg2[2] = 0;
			ret = sceMeCore_driver_278F2C25(5, arg2[3], arg2[4], arg2[16], arg2[17], arg2[18], arg2[19]);
			arg2[2] = 0;
			if (ret < 0){
				return sub_881F9560(arg2, ret);
			}
			return 0;
		break;
		case 2:
			if (arg0 != 1){
				return -1;
			}
			((u32*)arg2[4])[0] = arg2[9];
			((u32*)arg2[4])[1] = arg2[10];
			ret = sceMeCore_driver_278F2C25(38, arg2[3], arg2[4]);
			if (ret != 0){
				arg2[11] = 0;
				arg2[12] = 0;
				arg2[13] = 0;
				return sub_881F9590(arg2, ret);
			}
			arg2[2] = 0;
			arg2[11] = ((u32*)arg2[4])[12];
			arg2[12] = ((u32*)arg2[4])[13];
			arg2[13] = ((u32*)arg2[4])[14];
			return 0;
		break;
		case 3:
			if (arg0 == 0){
				sceMeCore_driver_278F2C25(6, &arg2[1]);
				return 0;
			}
			else if (arg0 == 1){
				arg2[1] = sceMeCore_driver_F702E73A(39);
				return 0;
			}
			return -1;
		break;
		case 5:
			if (arg0 != 0){
				return -1 ;
			}
			arg2[2] = 0;
			ret = sceMeCore_driver_278F2C25(9, arg2[3], arg2[20]);
			arg2[2] = 0;
			if (ret < 0){
				return sub_881F9560(arg2, ret);
			}
			return 0;
		break;
		case 6:
			if (arg0 != 0){
				return -1;
			}
			arg2[2] = 0;
			ret = sceMeCore_driver_278F2C25(10, arg2[3], arg2[21]);
			arg2[2] = 0;
			if (ret < 0){
				return sub_881F9560(arg2, ret);
			}
			return 0;
		break;
		case 7:
			if (arg0 != 0){
				return -1;
			}
			arg2[2] = 0;
			ret = sceMeCore_driver_278F2C25(11, arg2[3], arg2[22]);
			arg2[2] = 0;
			if (ret < 0){
				return sub_881F9560(arg2, ret);
			}
			return 0;
		break;
	}
	return 0xf00d;
}
/**************************sceMeAudio_driver****************************/
int sub_881F95BC(u32 codec, unsigned long *codec_buffer, int error){
	printf("sub_881F95BC\n");
	if (error == 0x80000003) return -1;
	u32 codecIndex = codec - 0x1000;
	if (codecIndex > 5) return -1;
	switch (codecIndex){
		case 0://at3+
			sceMeCore_driver_278F2C25(106, &codec_buffer[2], codec_buffer[3]);
			if (error>=256){
				codec_buffer[9] = 0;
			}
			return error<256?-3:-4;
		break;
		case 1://at3
			sceMeCore_driver_278F2C25(113, &codec_buffer[2], codec_buffer[3]);
			if (error>=256){
				codec_buffer[9] = 0;
			}
			return error<256?-3:-4;
		break;
		case 2://mp3
			codec_buffer[2] = (u32)error;
			return -3;
		break;
		case 3://aac
			sceMeCore_driver_278F2C25(145, &codec_buffer[2], codec_buffer[3]);
			if (error>=256){
				codec_buffer[9] = 0;
			}
			return error<256?-3:-4;
		break;
//		case 4:
//			return -1;
		break;
		case 5://wma
			sceMeCore_driver_278F2C25(230, &codec_buffer[2], codec_buffer[3]);
			if (error<0){
				codec_buffer[9] = 0;
			}
			return error>=0?-3:-4;
		break;
	}
	return -1;
}
//decode
int sceMeAudio_driver_310C9CDA(u32 codec, unsigned long *codec_buffer){
	unsigned long *cb17 = &codec_buffer[17];
	unsigned long *cb16 = &codec_buffer[16];
	void *memory = (void*)codec_buffer[3];
	void *data = (void*)codec_buffer[6];
	u32 samples = codec_buffer[8];
	if (codec_buffer[0] > 0x05100601) return -2;
	if (codec_buffer[3] == 0) return 0x80000103;
	codec_buffer[9] = 0;
	codec_buffer[2] = 0;
	sceKernelDcacheWritebackInvalidateRange(codec_buffer, 104);
	u32 codecIndex = codec - 0x1000;
	if (codecIndex > 5) return -1;
	int ret = 0;
	switch (codecIndex){
		case 0:
			ret = sceMeCore_driver_F702E73A(96, codec_buffer);
		break;
		case 1:
			ret = sceMeCore_driver_F702E73A(112, codec_buffer);
		break;
		case 2:
			ret = sceMeCore_driver_F702E73A(140, codec_buffer);
		break;
		case 3:
//			ret = sceMeCore_driver_F702E73A(144, codec_buffer[6], &codec_buffer[7], codec_buffer[8], &codec_buffer[9], codec_buffer[3]);
			ret = sceMeCore_driver_F702E73A(144, data, &codec_buffer[7], samples, &codec_buffer[9], memory);
		break;
		default:
		case 4:
			return -1;
		break;
		case 5:
//			ret = sceMeCore_driver_F702E73A(229, codec_buffer[6], codec_buffer[15], &codec_buffer[16], codec_buffer[8], &codec_buffer[9], &codec_buffer[17], codec_buffer[3]);
			ret = sceMeCore_driver_F702E73A(229, data, codec_buffer[15], cb16, samples, &codec_buffer[9], cb17, memory);
		break;
	}
	if (ret < 0){
		return sub_881F95BC(codec, codec_buffer, ret);
	}
	return 0;
}
//check need memory
int sceMeAudio_driver_BB9821AC(u32 codec, unsigned long *codec_buffer){
	if (0x5100601 < codec_buffer[0]) return -2;
	u32 codecIndex = codec - 0x1000;
	if (codecIndex > 5) return -1;
	int ret = 0;
	switch (codecIndex){
		case 0://at3+
			ret = sceMeCore_driver_278F2C25(99, &codec_buffer[13], &codec_buffer[15], &codec_buffer[16], &codec_buffer[10]);
			if (ret >= 0){
				codec_buffer[2] = 0;
				ret = sceMeCore_driver_278F2C25(102, codec_buffer[15], &codec_buffer[4], &codec_buffer[2]);
				if (ret == 0){
					return 0;
				}
				return -3;
			}
		break;
		case 1://at3
			/* how could i mistake 114 for 144 */
			ret = sceMeCore_driver_278F2C25(114, 2, &codec_buffer[4], &codec_buffer[2]);
			if (ret == 0){
				return 0;
			}
			return -3;
		break;
		case 2://mp3
			ret = sceMeCore_driver_278F2C25(138, 2, &codec_buffer[4], &codec_buffer[2]);
			if (ret == 0){
				return 0;
			}
			return -3;
		break;
		case 3://aac
			codec_buffer[4] = sceMeCore_driver_F702E73A(146);//=0x648c
			codec_buffer[2] = 0;
			return 0;
			break;
		case 4://not supported
			return -1;
		break;
		case 5://wma
			codec_buffer[4] = sceMeCore_driver_F702E73A(226);
			codec_buffer[2] = 0;
			return 0;
		break;
	}
	return sub_881F95BC(codec, codec_buffer, ret);
}
int unk[] = {//guessed, codec_buffer[10] should be 4 or 6, got 8744 from a snd0 file
			0x00000000,
			0x00000100,
			0x00000001,
			0x00000100,
			0x00000000,
			0x000000D4,
			0x00000001,
			0x000000D4,
			0x00000000,
			0x000000C0,
			0x00000001,
			0x000000C0,
			0x00000000,
			0x00000098,
			0x00000001,
			0x00000098,
			0x00000000,
			0x00000088,
			0x00000001,
			0x00000088,
			0x00000000,
			0x00000060,
			0x00000001,
			0x00000060,
			0x00000001,
			0x00000044,
			0x00000001,
			0x00000030,
			0x00000002,
			0x000000C0,
			0x00000002,
			0x00000098};
//init
int sceMeAudio_driver_F5F87C12(u32 codec, unsigned long *codec_buffer){
	if (0x05100601 < codec_buffer[0]) return -2;
	u32 codecIndex = codec - 0x1000;
	codec_buffer[2] = 0;
	if (codecIndex > 5) return -1;
	int ret = 0;
	switch (codecIndex){
		case 0://at3+
			ret = sceMeCore_driver_278F2C25(99, &codec_buffer[13], &codec_buffer[15], &codec_buffer[16], &codec_buffer[10]);
			if (ret >= 0){
				int val = codec_buffer[15];
				switch (val){
					case 0:
					default :
						codec_buffer[2] = 514;
						return -3;
					break;
					case 1:
						val = 2;
					break;
					case 2:
					case 3:
					case 4:
					break;
					case 5:
					case 6:
					case 7:
						val++;
					break;
				}
				codec_buffer[18] = 2;
				ret = sceMeCore_driver_278F2C25(103, codec_buffer[13], codec_buffer[15], codec_buffer[16], 2, codec_buffer[3]);
				if (ret >= 0){
					ret = sceMeCore_driver_278F2C25(105, &codec_buffer[11], codec_buffer[3]);
					if (ret >= 0){
						ret = sceMeCore_driver_278F2C25(100, &codec_buffer[17], codec_buffer[3]);
						if (ret >= 0){
							ret = sceMeCore_driver_F702E73A(104, codec_buffer[5], codec_buffer[3]);
						}
					}
				}
			}
		break;
		case 1://at3
			codec_buffer[11] = 44100;
			codec_buffer[12] = unk[codec_buffer[10]*2+1];
			ret = sceMeCore_driver_278F2C25(115, unk[codec_buffer[10]*2], 44100, unk[codec_buffer[10]*2+1], codec_buffer[3]);
			if ((codec_buffer[10]-14)<2){
				codec_buffer[13] = 2;
			}
		break;
		case 2://mp3
			ret = sceMeCore_driver_278F2C25(139, codec_buffer[3]);
		break;
		case 3://aac
			switch (codec_buffer[10]){
				case 96000:
				case 88200:
				case 64000:
				case 48000:
				case 44100:
				case 32000:
				case 24000:
				case 22050:
				case 16000:
//				case 12000: not supported
				case 11050:
				case 8000:
					ret = sceMeCore_driver_278F2C25(147, codec_buffer[10], codec_buffer[3]);
					if (ret >= 0){					
						if (((short*)codec_buffer)[22] != 0){
							ret = sceMeCore_driver_278F2C25(149, ((u8*)codec_buffer)[44], codec_buffer[3]);
							if (ret >= 0){
								if (((char*)codec_buffer)[45] != 0){
									ret = sceMeCore_driver_278F2C25(151, 0, 2, 0, codec_buffer[3]);
								}
							}
						}
						else{
							if (((char*)codec_buffer)[45] != 0){
								ret = sceMeCore_driver_278F2C25(151, 0, 2, 0, codec_buffer[3]);
							}
						}
					}
				break;
				default:
					return -1;
				break;
			}			
		break;
		case 4://not used
			return -1;
		break;
		case 5://wma
			ret = sceMeCore_driver_278F2C25(227, &codec_buffer[10], codec_buffer[3]);
		break;
	}
	if (ret < 0){
		return sub_881F95BC(codec, codec_buffer, ret);
	}
	return ret;	
}
//probably for umd(only at3/+)
int sceMeAudio_driver_24AFB7E1(u32 codec, unsigned long *codec_buffer){//3D6CC0D9
	printf("sceMeAudio_driver_24AFB7E1\n");
	if (0x5100601 < codec_buffer[0]) return -2;
	codec_buffer[2] = 0;
	int ret;
	if (codec == 0x1000){
		ret = sceMeCore_driver_278F2C25(99, &codec_buffer[13], &codec_buffer[15], &codec_buffer[16], &codec_buffer[10]);
		if (ret >= 0){
			codec_buffer[2] = 0;
			if (codec_buffer[15] != -1){
				return -1;
			}
			codec_buffer[18] = codec_buffer[15];
			ret = sceMeCore_driver_278F2C25(103, codec_buffer[13], 1, codec_buffer[16], 1, codec_buffer[3]);
			if (ret >= 0){
				ret = sceMeCore_driver_278F2C25(105, &codec_buffer[11], codec_buffer[3]);
				if (ret >= 0){
					ret = sceMeCore_driver_278F2C25(100, &codec_buffer[17], codec_buffer[3]);
					if (ret >= 0){
						ret = sceMeCore_driver_F702E73A(104, codec_buffer[5], codec_buffer[3]);
					}
				}
			}
		}
	}
	else if (codec == 0x1001){
		if ((u32)(codec_buffer[10]-14) >= 2){
			return -1;
		}
		codec_buffer[13] = 1;
		codec_buffer[12] = unk[codec_buffer[10]*2+1];
		ret = sceMeCore_driver_278F2C25(115, unk[codec_buffer[10]*2], 44100, unk[codec_buffer[10]*2+1], codec_buffer[3]);
	}
	else{
		return -1;
	}
	if (ret < 0){
		return sub_881F95BC(codec, codec_buffer, ret);
	}
	return ret;
}
//get info?
int sceMeAudio_driver_7C8ADD5E(u32 codec, u32 arg1, unsigned long *codec_buffer){
	printf("sceMeAudio_driver_7C8ADD5E\n");
	if (0x5100601 < codec_buffer[0]) return -2;
	codec_buffer[2] = 0;
	int ret;
	if (arg1 == 3){
		u32 codecIndex = codec - 0x1000;
		int index;
		switch(codecIndex){
			case 0:
				index = 97;//ret = 0x20400
			break;
			case 1:
				index = 116;//ret = 0x30600
			break;
			case 2:
				index = 129;//ret = 0x30600
			break;
			case 3:
				index = 148;//ret = 0x50021
			break;
			case 4:
			default:
				return -1;
			break;
			case 5:
				index = 225;//ret = 0x80000003 need to call sceMeBootStart(3) before
			break;
		}
		ret = sceMeCore_driver_F702E73A(index);
		codec_buffer[1] = ret;
		return ret;
	}
	else if (arg1 >= 4){
		if (arg1 != 4){
			return -1;
		}
		if (codec != 4096){
			return -1;
		}
		//at3+ checkneed mem
		ret = sceMeCore_driver_278F2C25(99, &codec_buffer[13], &codec_buffer[15], &codec_buffer[16], &codec_buffer[10]);
		if (ret < 0){
			return sub_881F95BC(codec, codec_buffer, ret);
		}
		return ret;
	}
	else if (arg1 == 2){
		if (codec != 4098){
			return -1;
		}
		ret = sceMeCore_driver_278F2C25(137, codec_buffer[10], codec_buffer[3]);
		if (ret == 0){
			ret = sceMeCore_driver_278F2C25(130, codec_buffer[6], &codec_buffer[11], &codec_buffer[14], codec_buffer[3]);
		}
		if (ret < 0){
			return sub_881F95BC(codec, codec_buffer, ret);
		}
		return ret;
	}
	return -1;
}
/**************************sceMeMemory_driver****************************/
void *sceMeMalloc(int size){
	return (void*)sceMeCore_driver_F702E73A(384, size);
}
void *sceMeCalloc(int num, int size){
	return (void*)sceMeCore_driver_F702E73A(386, num, size);
}
void sceMeFree(void *ptr){
	sceMeCore_driver_F702E73A(385, ptr);
}
/**************************sceMeCore_driver****************************/
int sceMeRpcLock(){
	return sceKernelLockMutex(meRpc.mutex, 1, 0);
}
int sceMeRpcUnlock(){
	return sceKernelUnlockMutex(meRpc.mutex, 1);
}
//me*img.img contains 2 sets of compressed data(except maybe sd), code and contents of main memory
void decompressAndRunMeImage(void *data){
	void *end = data;
	int ret;
	if (0x3fffff < ((u32)data & ~0x88000000)){
		((void (*)(void*))0x88300000)(end);
	}
	*(int*)0xbfc00700 = -4;
	if (memcmp(data, "KL4E", 4) == 0){
		ret = UtilsForKernel_6C6887EE((void*)0x88300000, (void*)((u32)data & ~0x88300000), data + 4, &end);//decompress meimg.img
		if (ret >= 0){
			sceKernelDcacheWritebackInvalidateAll()	;
			sceKernelIcacheInvalidateAll();
			((void (*)(void*))0x88300000)(end);
		}
	}
	sceKernelMemset32(end, 0, (void*)0x88400000 - (u32)end);
	*(int*)0xBFC00700 = -5;	
	*(int*)0xBFC00710 = -2;
	//dead end
	asm volatile("haltLoop:\n"
				 ".word 0x70000000\n"
				 "b haltLoop\n"
				 "nop\n");
}
int sub_881FA1AC(void *data, int size){
	int ret, newSize;
	//decryption won't be nessasary, as encryption is not possible or needed
	ret = sceWmd_driver_7A0E484C(data, size, &newSize);
	if (ret < 0){
		return ret;
	}
	return newSize;
}
int sub_881FA1D8(void* data, int wait){
	sceSysregMeResetEnable();
	sceSysregMeBusClockEnable();
	int size = (u32)me_boot_code_end - (u32)me_boot_code;
	memcpy((void*)0xbfc00040, (void*)me_boot_code, size);
	*(int*)0xbfc00060 = *(int*)0xbfc00060 | (*(int*)0xbc100040 & 3);//li $t, [0,1,2,3]
	*(int*)0xbfc00064 = *(int*)0xbfc00064 | ((u32)data >> 16);
	*(int*)0xbfc00068 = *(int*)0xbfc00068 | ((u32)data & 0xffff);
	sceKernelDcacheWritebackInvalidateAll();
	sceDdrFlush(4);
	*(int*)0xbfc0070c = 0x1ff01ff;
	*(int*)0xbfc00714 = 2;
	volatile int *hwAddress = (int*)0xbfc00700;
	*hwAddress = -3;
	sceSysregAvcResetDisable();
	sceSysregVmeResetDisable();
	sceSysregMeResetDisable();
	if (wait){
		while (*hwAddress == -3){
			sceKernelDelayThread(1000);
		}
	}
	*hwAddress = 0;
	sceDdrFlush(8);
	return 0;
}
int sceMeBootStart(u32 arg){
	printf("sceMeBootStart %d\n", arg);
	if (arg >= 5) return 0x80000102;
	int genArg = arg;
	u32 tachyon = sceSysregGetTachyonVersion();
	if (tachyon > 0x4fffff){//if not first generation
		if (genArg != 2){
			genArg = 3;
		}	
//		genArg ^= 2;
//		genArg = genArg?3:2;
	}
	if (genArg == meStarted){
		return 0;
	}
	int ret =  sceMeRpcLock();
	if (ret < 0){
		return ret;
	}
	if (((genArg == 3)&&(meStarted == 2)) || ((genArg == 2)&&(meStarted == 3))){
			meStarted = genArg;
			sceMeRpcUnlock();
			sceMeCore_driver_F702E73A(389, (u32)(genArg^2)>0);//genArg==2?0:1
			return 0;
	}
	char *meImage;
	if (tachyon > 0x4fffff){
		meImage = "flash0:/kd/resource/me_t2img.img";
	}
	else if (genArg == 1){
		meImage = "flash0:/kd/resource/me_sdimg.img";
	}
	else if (genArg == 4){
		meImage = "flash0:/kd/resource/me_blimg.img";
	}
	else{
		meImage = "flash0:/kd/resource/meimg.img";
	}
	SceUID fd = sceIoOpen(meImage, PSP_O_UNKNOWN0|PSP_O_RDONLY, 0);
	if (fd < 0){
		sceMeRpcUnlock();
		return fd;
	}
	int size = sceIoLseek(fd, 0, PSP_SEEK_END);
	sceIoLseek(fd, 0, PSP_SEEK_SET);
	if (size > 0x5ffff){//charge to actual size when me image is written
		sceIoClose(fd);
		sceMeRpcUnlock();
		return 0x80000022;
	}
	sceSysregMeResetEnable();
	void *address = (void*)((u32)0x883ff000 - (size & 0xffffffc0));
	int read = sceIoRead(fd, address, size);
	sceIoClose(fd);
	if (read != size){
		sceMeRpcUnlock();
		return (read<0)?read:0x80000022;
	}
	sub_881FA1AC(address, read);
	sub_881FA1D8(address, 1);
	meStarted = genArg;
	sceMeRpcUnlock();
	sceMeCore_driver_F702E73A(389, ((u32)(genArg^2))>0);
	return 0;
}
/*called
sceMeBootStart
sceMeCore_driver_278F2C25
sceMeCore_driver_F702E73A
sceMePowerControlAvcPower
sceMePower_driver_23481212
sceMePower_driver_52E7CD99
sceMePower_driver_68B70336
sceMeVideo_driver_2C38DB84
sceMeVideo_driver_2DAAC3A9
sceMeVideo_driver_6401013A//umd video
sceMeVideo_driver_93EED793
sceMeVideo_driver_994BBA92
sceMeVideo_driver_F24820D4
sceMeAudio_driver_310C9CDA
sceMeAudio_driver_BB9821AC
sceMeAudio_driver_F5F87C12
*/

